//
// Created by ht on 16-12-5.
//

#include "symtab.h"
#include "gtree.h"
#include <iostream>

using namespace std;
extern vector<Field> fields;
extern vector<Function> functions;
extern vector<Struct> structs;
extern vector<Array> params;
extern int param_offset;
void Error(int type, int line, string msg) {
    cout << "Error type " << type << " @ " << line << ": " << msg << endl;
}
string get_tag();
int refer_param(GTNode* exp){
    GTNode *id = exp->child;
    int offset=0;
    for(int i=0;i<params.size();i++){
        if(params[i].name==id->name){
            if(params[i].type&T_STRUCT){
                exp->struct_name=params[i].struct_name;
            }
            if(params[i].type&T_ARR){
                exp->dimen=params[i].dimen;
                exp->sizes=params[i].sizes;
            }
            exp->type=params[i].type;
            exp->offset=params[i].offset;
            return 1;
        }
        offset+=4;
    }
    return 0;
}
void refer_var(GTNode *exp) {
    GTNode *id = exp->child;
    for (int i = fields.size() - 1; i >= 0; --i) {
        Field f = fields[i];
        for (int j = 0; j < f.primitives.size(); j++) {
            if (f.primitives[j].name == id->name) {
                exp->type = f.primitives[j].type;
                exp->offset=f.primitives[j].offset+param_offset;
                if (exp->type & T_STRUCT) {
                    exp->struct_name = f.primitives[j].struct_name;
                }
                return;
            }
        }
        for (int j = 0; j < f.arrays.size(); j++) {
            if (f.arrays[j].name == id->name) {
                exp->type = f.arrays[j].type;
                exp->offset=f.arrays[j].offset+param_offset;
                if (exp->type & T_STRUCT)
                    exp->struct_name = f.arrays[j].struct_name;
                exp->dimen = f.arrays[j].dimen;
                exp->sizes = f.arrays[j].sizes;
                return;
            }
        }
        if(refer_param(exp))
            return;
    }
    Error(1, id->lineno, "Variable '" + id->name + "' not defined");
}

void refer_struct(GTNode *exp) {
    GTNode *structp = exp->child;
    if (structp->type != T_STRUCT) {
        Error(13, structp->lineno, "not a struct");
        return;
    } else {
        string stru_name = structp->struct_name;
        string field = structp->sibiling->sibiling->id;
        for (int i = 0; i < structs.size(); i++) {
            Struct s = structs[i];
            if (stru_name == s.name) {
                for (int j = 0; j < s.fields.size(); j++) {
                    if (s.fields[j].name == field) {
                        exp->type = s.fields[j].type;
                        return;
                    }
                }
                Error(14, structp->lineno, "accessing undefined field '" + field + "'");
                return;
            }
        }
    }
}
void refer_array(GTNode *exp) {
    GTNode *array = exp->child;
    if (array->type & T_ARR) {
        GTNode* index=exp->child->sibiling->sibiling;
        if(array->type&T_STRUCT)
            exp->struct_name=array->struct_name;
        if (index->type != T_INT&&index->type!=T_NUM_INT) {
            Error(12, exp->lineno, "invalid index");
            return;
        }
        exp->dimen = array->dimen - 1;
        if (exp->dimen == 0) {
            exp->type = Type(array->type & ~T_ARR);
            return;
        } else if (exp->dimen > 0) {
            exp->type = array->type;
            exp->sizes=array->sizes;
            return;
        }
    }
    Error(10, exp->lineno, exp->child->child->name + " is not an array");

}

void refer_func_no_param(GTNode *exp) {
    string func_name = exp->child->id;
    for (int i = 0; i < functions.size(); i++) {
        if (functions[i].name == exp->child->id) {
            exp->offset=functions[i].offset;
            exp->dimen=0;
            if (functions[i].params.size() > 0) {
                Error(9, exp->lineno, "parameters dont match");
            } else {
                exp->type=functions[i].return_type;
                if((exp->type)&T_STRUCT)
                    exp->struct_name=functions[i].return_struct_name;
                if((exp->type)&T_ARR){
                    //Todo return array
                }
            }
            return;
        }
    }
    Error(2, exp->lineno, "referring undefined function '" + func_name + "'");
}

void refer_func_with_param(GTNode *exp) {
    GTNode *param = exp->child->sibiling->sibiling->child;
    string func_name = exp->child->id;
    for (int i = 0; i < functions.size(); i++) {
        if (functions[i].name == func_name) {
            exp->offset=functions[i].offset;
            exp->dimen=functions[i].param_offset;
            for (int j = 0; j < functions[i].params.size(); j++) {
                if (param->type != functions[i].params[j].type) {
                    if(!(functions[i].params[j].type==T_INT&&param->type==T_NUM_INT||functions[i].params[j].type==T_FLOAT&&param->type==T_NUM_FLOAT)) {
                        Error(9, exp->lineno, "parameters dont match");
                        return;
                    }
                }
                if (param->type & T_STRUCT) {
                    if (param->struct_name != functions[i].params[j].struct_name) {
                        Error(9, exp->lineno, "parameters dont match");
                        return;
                    }
                }
                if (!param->sibiling) {//调用列表中无参数了
                    if (functions[i].params.size() - j > 1) {
                        Error(9, exp->lineno, "parameters dont match");
                        return;
                    }
                }else{//调用列表中还有参数
                    if (functions[i].params.size() - j == 0) {//如果函数定义再无参数了
                        Error(9, exp->lineno, "parameters dont match");
                        return;
                    }
                    param = param->sibiling->sibiling->child;
                }
            }
            exp->type=functions[i].return_type;
            if((exp->type)&T_STRUCT)
                exp->struct_name=functions[i].return_struct_name;
            if((exp->type)&T_ARR){
                //Todo return array
            }
            return;
        }
        exp->dimen=param_offset;
    }
    Error(2, exp->lineno, "referring undefined function '" + func_name + "'");
}

void basic_op_check(GTNode *r, GTNode *op1, GTNode *op2) {
    if (!((op1->type == T_INT || op1->type == T_FLOAT)&& (op2->type==T_INT||op2->type==T_FLOAT||op2->type==T_NUM_FLOAT||op2->type==T_NUM_INT))){
        Error(7, op1->lineno, "operation not allowed");
        r->type = T_ERR;
    } else {
        r->type = op1->type;
    }

}

void logic_op_check(GTNode *r, GTNode *op1, GTNode *op2) {
    if (op1->type != T_INT || op1->type != op2->type) {
        Error(11, op1->lineno, "logic operation on invalid type");
    } else {
        r->type = T_BOOL;
    }
}

void assign_op_check(GTNode *r, GTNode *op1, GTNode *op2) {
    if (op1->type == T_NUM_INT || op1->type == T_NUM_FLOAT || op1->type == T_BOOL) {
        Error(6, r->lineno, "assign to right value");
        r->type = T_ERR;
        return;
    }
    if (op1->type & T_ARR) {
        Type t1 = Type((op1->type) & ~T_ARR);
        Type t2 = Type((op2->type) & ~T_ARR);
        if (t1 == t2) {
            if (t1 == T_STRUCT) {
                if (op1->struct_name != op2->struct_name) {
                    goto ERROR;
                }
            }
            if (op1->dimen == op2->dimen) {
                int j;
                for (j = 0; j < op1->dimen; j++) {
                    if (op1->sizes[j] != op2->sizes[j])
                        break;
                }
                if (j == op1->dimen) {//类型匹配
                    r->type = op1->type;
                    return;
                }
            }
        }

    }
    if (op1->type == op2->type||op1->type==T_INT&&op2->type==T_NUM_INT||op1->type==T_FLOAT&&op2->type==T_NUM_FLOAT) {
        if (op1->type == T_INT || op1->type == T_FLOAT){
            r->type = op1->type;
            return;
        }
        if (op1->type == T_STRUCT) {
            if (op1->struct_name == op2->struct_name) {
                r->struct_name = op1->struct_name;
                r->type = T_STRUCT;
                return;
            }
        }
    }
    ERROR:
    Error(5, op1->lineno, "assign type mismatch");
    r->type = T_ERR;
}
